﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazz is open software: you can redistribute it and/or modify it 
// under the terms of the GNU Affero General Public License (AGPL) as published by 
// the Free Software Foundation, version 3 of the License.
// 
// Jazz is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty 
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
//  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public 
// License along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
using System;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TrackerRealm.Jazz.Client;
using PostSharp.Extensibility;
using TrackerRealm.Jazz.Client.Custom;

namespace JazzClientTest
{
    /// <summary>
    /// A test suite that demonstrates using of the Role within Jazz.
    /// This builds on "1.StateTests"
    /// 
    /// For more information on roles see - http://wiki.jazzfire.net/Role.ashx
    /// </summary>
    [TestClass]
    public class RoleTests
    {
        #region ApproveWorkflow_Roles_HappyPath_Verbose
        /// <summary>
        /// This demonstrates a correct path of execution.
        /// That is data is entered, then it is approved.
        /// See the Console output to see the details of the operations
        /// </summary>
        [TestMethod]
        public void ApproveWorkflow_Roles_HappyPath_Verbose()
        {
            NexusConfig config = new NexusConfig()
            {
                RoleCheckCollection = new string[] { "Manager" },
                Verbose = VerboseType.All,
            };
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                Assert.AreEqual("Guest", nexus.LoginProfile.User.Name);
                Assert.AreEqual(0, nexus.LoginRoles.Count());

                RoleRequest myRequest = new RoleRequest(nexus);
                //
                // Enter some data - this will change the state the Approval state.
                myRequest.Request = "May I leave early on Friday?";
                //
                // change user
                //
                using(LoggedinUser loginUser = nexus.ChangeLoginProfile(jUser.Create("Joe", "Manager")))
                {
                    Assert.AreEqual("Joe", nexus.LoginProfile.User.Name);
                    Assert.AreEqual(1, nexus.LoginRoles.Count());
                    Assert.IsTrue(nexus.LoginRoles.Contains("Manager"));
                    //
                    // Approve the workflow
                    myRequest.Approve();
                }
            }
        }
        #endregion
        #region ApproveWorkflow_ErrorConditions_Verbose
        /// <summary>
        /// This demonstrates that an exception occurs when access is not allowed.
        /// </summary>
        [TestMethod]
        [TestCategory("role")]
        public void ApproveWorkflow_ErrorConditions_Verbose()
        {
            NexusConfig config = new NexusConfig()
            {
                RoleCheckCollection = new string[] { "Manager" },
                Verbose = VerboseType.All,
            };
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                RoleRequest myRequest = new RoleRequest(nexus);

                //
                // Enter some data - this will change the state the Approval state.
                myRequest.Request = "May I leave early on Friday?";

                try
                {
                    //
                    // Approve the workflow
                    // This will cause an exception since the 
                    // logged in user does not have the appropriate roles
                    myRequest.Approve();
                }
                catch (Exception ex)
                {
                    Assert.IsTrue(ex is AccessViolationException);
                }

                // The request has not be approved.
                Assert.IsFalse(myRequest.IsApproved);
            }
        }
        #endregion
        #region RoleExample_WhichMembersAreAccessible
        /// <summary>
        /// This demonstrates which members are accessible in which state.
        /// This is useful for an application that would like to display operations and attributes of a workflow.
        /// </summary>
        [TestMethod]
        public void RoleExample_WhichMembersAreAccessible()
        {
            NexusConfig config = new NexusConfig()
            {
                RoleCheckCollection = new string[] { "Manager" }
            };
            using (ClientNexus nexus = ClientNexus.Create(config))
            {
                RoleRequest myRequest = new RoleRequest(nexus);
                //
                // begin in the 'Start' State
                Assert.AreEqual("Start", myRequest.CurrentState.Name);
                //
                //  Start State
                //        --> Request
                //           --> CanRead
                //           --> CanWrite
                //        --> IsApproved
                //           --> CanRead
                //
                jMemberInfo[] members = myRequest.GetWrappedMembers().UI().ToArray();
                Assert.AreEqual(3, members.Length);
                jMemberInfo request = members.First(m => m.Name == "Request");
                jMemberInfo isApproved = members.First(m => m.Name == "IsApproved");
                jMemberInfo approve = members.First(m => m.Name == "Approve");

                // 'Request' property
                Assert.IsTrue(request.CanRead);
                Assert.IsTrue(request.CanWrite);
                // 'IsApproved' property
                Assert.IsTrue(isApproved.CanRead);
                Assert.IsTrue(isApproved.WriteAccessor.IsEmpty);
                // 'Approve' method
                Assert.IsTrue(approve.IsGrey);
                //
                // Enter some data - this will change the state the Approval state.
                request.Write("May I leave early on Friday?");
                Assert.AreEqual("Approval", myRequest.CurrentState.Name);
                Assert.AreEqual("May I leave early on Friday?", request.Read());
                //
                // change user
                //
                nexus.ChangeLoginProfile(jUser.Create("Joe", "Manager"));
                //
                //  Approval State
                //        --> Request
                //           --> CanRead
                //        --> IsApproved
                //           --> CanRead
                //        --> Approve
                //

                // 'Request' property
                Assert.IsTrue(request.CanRead);
                Assert.IsTrue(request.WriteAccessor.IsGrey);
                // 'IsApproved' property
                Assert.IsTrue(isApproved.CanRead);
                Assert.IsTrue(isApproved.WriteAccessor.IsEmpty);
                // 'Approve' method
                Assert.IsTrue(approve.IsPublic);
                
                //
                // Approve the workflow
                approve.Execute();
                //
                // Now the workflow is in the Completed State.
                Assert.AreEqual("Completed", myRequest.CurrentState.Name);
                //
                //  Completed State
                //        --> Request
                //           --> CanRead
                //        --> IsApproved
                //           --> CanRead
                //

                // 'Request' property
                Assert.IsTrue(request.CanRead);
                Assert.IsTrue(request.WriteAccessor.IsGrey);
                // 'IsApproved' property
                Assert.IsTrue(isApproved.CanRead);
                Assert.IsTrue(isApproved.WriteAccessor.IsEmpty);
                // 'Approve' method
                Assert.IsTrue(approve.IsGrey);
            }
        }
        #endregion
        #region RoleRequest Class - Used in all tests
        /// <summary>
        /// This is a small class that demonstrates the use of states and roles in
        /// a personel request.
        /// The class is very simple only allowing a request to made 
        /// by one user and then approved by a another user with the role of 'Manager'.
        /// ***
        /// This class builds on 'SimpleRequest' class in the '1.StateTests' by adding
        /// a 'GrantRole' to the Approve method.
        /// </summary>
        [Jazz]
        public class RoleRequest : jObject
        {
            #region Constructor
            /// <summary>
            /// Constructor - Auto binds
            /// </summary>
            public RoleRequest(ClientNexus nexus)
            :base(nexus) // causes auto binding when this object is created
            {
                this.data = "";
                this.IsApproved = false;
                this.EnterState("Start");
            }
            #endregion
            #region Request
            private string data;
            /// <summary>
            /// Make a request by setting the data.
            /// </summary>
            [UI]
            public string Request
            {
                get { return this.data; }
                [GrantStates("Start")]
                set
                {
                    this.data = value;
                    // prevent the data from being changed.
                    this.EnterState("Approval");
                }
            }
            #endregion
            #region IsApproved
            /// <summary>
            /// Returns true when the Request has been approved.
            /// </summary>
            [UI]
            public bool IsApproved
            {
                get;
                private set;
            }
            #endregion
            #region Approve
            /// <summary>
            /// The approve method is only allowed to be executed 
            /// when this instance's current state is 'Approval'
            /// and the logged in user has the role 'Manager'.
            /// </summary>
            [UI]
            [GrantStates("Approval")]
            [GrantRoles("Manager")]
            public void Approve()
            {
                this.IsApproved = true;
                // prevent the workflow from being approved again.
                this.EnterState("Completed");
            }
            
            #endregion
            #region States
            [StartState]
            void Start()
            {
            }

            [State]
            void Completed()
            {
                // this will be written to the console when the state is entered.
                Console.WriteLine("***** All Done ******");
            }
            [FinalState]
            void Approval()
            {
            }
            #endregion
        }
        #endregion
    }
}
